using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Diagnostics;

namespace Sio.Mdm.Utils.Collections
{
    /// <summary>
    /// Dictionary that allows accessing items with index
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>    
    [CLSCompliant(true)]
    [Serializable]
    [DebuggerDisplay("Count = {Count}")]
    public class IndexedDictionary<TKey, TValue> : BaseDictionary<TKey, TValue>, IEnumerable<TValue>
    {
        #region Fields
        private List<TKey> _keys = new List<TKey>();
        private List<TValue> _values = new List<TValue>();        
        #endregion

        #region Construction
        /// <summary>
        /// Initialize Indexed dictionary
        /// </summary>
        public IndexedDictionary()
        {
        }
        #endregion

        #region Properties

        #region Private
        private ListEnumerator _enumerator;
        private ListEnumerator InternalEnumerator
        {
            get
            {
                if (null == _enumerator)
                {
                    _enumerator = new ListEnumerator(_values);
                }

                return _enumerator;
            }
        }
        #endregion

        #endregion

        #region Methods

        #region Public

        #region Indexer
        /// <summary>
        /// Get item by index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public TValue this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                {
                    throw new ArgumentOutOfRangeException("index");
                }

                return base[_keys[index]];
            }
            set
            {
                if (index < 0 || index >= Count)
                {
                    throw new ArgumentOutOfRangeException("index");
                }

                base[_keys[index]] = value;
            }
        }
        #endregion

        #region IndexOf
        /// <summary>
        /// Get index of specified key
        /// </summary>
        /// <param name="key">Key to retreive index for</param>
        /// <returns>Index of key or -1 if key is not in dictionary</returns>
        public int IndexOfKey(TKey key)
        {
            return _keys.IndexOf(key);
        }
        /// <summary>
        /// Get index of specified value
        /// </summary>
        /// <param name="value">Value to retreive index for</param>
        /// <returns>Zero base index of value or -1 if value is not present</returns>
        public int IndexOfValue(TValue value)
        {
            return _values.IndexOf(value);
        }
        #endregion

        #region IEnumerable<TValue> Members
        /// <summary>
        /// Get list neumerator for indexed dictionary
        /// </summary>
        /// <returns></returns>
        public new IEnumerator<TValue> GetEnumerator()
        {
            return new ListEnumerator(_values);
        }
        #endregion

        #region Insert
        /// <summary>
        /// Insert item and key to dictionary at specified index
        /// </summary>
        /// <param name="index">Zero based index to insert item at</param>
        /// <param name="key">Key to insert</param>
        /// <param name="value">Item to insert</param>
        public void Insert(int index, TKey key, TValue value)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().ToString());
            }

            if (base.ContainsKey(key))
            {
                throw new ArgumentException(String.Format("Key '{0}' already exist in dictionary.", key));
            }

            base.Add(key, value);
        } 
        #endregion

        #region RemoveAt
        /// <summary>
        /// Remove item at specified index
        /// </summary>
        /// <param name="index">Index of item to remove</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Index is less than 0.
        /// -or-
        /// Index is bigger than count.
        /// </exception>
        public void RemoveAt(int index)
        {
            if (index < 0 || index >= Count)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            base.Remove(_keys[index]);
        }
        #endregion

        #region KeyAt
        /// <summary>
        /// Get key at specified index
        /// </summary>
        /// <param name="index">Index of key to retreive</param>
        /// <returns>Key at specified index</returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Index is greater or equal to count
        ///  - or -
        /// Index is less than zero
        /// </exception>
        public TKey KeyAt(int index)
        {
            if (index < 0 || index >= Count)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            return _keys[index];
        }
        #endregion

        #endregion

        #region Protected

        #region OnAfterAdd
        /// <summary>
        /// Overriden to store index of specifi key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        protected override void OnAfterAdd(TKey key, TValue value)
        {
            _keys.Add(key);
            _values.Add(value);
            base.OnAfterAdd(key, value);          
        } 
        #endregion

        #region OnAfterRemove
        /// <summary>
        /// Overriden to remove index of specifi key
        /// </summary>
        /// <param name="key">Key to be removed</param>
        /// <param name="value">Value of key to be removed</param>
        protected override void OnAfterRemove(TKey key, TValue value)
        {
            int index = _keys.IndexOf(key);
            _keys.RemoveAt(index);
            _values.RemoveAt(index);
            base.OnAfterRemove(key, value);
        }         
        #endregion

        #region OnAfterSet
        /// <summary>
        /// Overriden to reasign value in indexed list
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        protected override void OnAfterSet(TKey key, TValue value)
        {
            _values[_keys.IndexOf(key)] = value;
            base.OnAfterSet(key, value);
        } 
        #endregion

        #region OnAfterClear
        /// <summary>
        /// Overriden to clear indexes of keys
        /// </summary>
        protected override void OnAfterClear()
        {
            _keys.Clear();
            _values.Clear();
            base.OnAfterClear();
        } 
        #endregion

        #region OnKeyReplaced
        /// <summary>
        /// Overriden to fix index of key
        /// </summary>
        /// <param name="oldKey"></param>
        /// <param name="newKey"></param>
        protected override void OnKeyReplaced(TKey oldKey, TKey newKey)
        {
            _keys[_keys.IndexOf(oldKey)] = newKey;
            base.OnKeyReplaced(oldKey, newKey);
        } 
        #endregion

        #endregion

        #endregion

        #region Classes
        /// <summary>
        /// Implementation of enumerator for using foreach on values
        /// </summary>
        public sealed class ListEnumerator : IEnumerator<TValue>
        {
            #region Fields
            private List<TValue> Dictionary;
            private int Index = -1;
            #endregion

            #region Construction
            internal ListEnumerator(List<TValue> dictionary)
            {
                Dictionary = dictionary;
            }
            #endregion

            #region Properties

            #region Public

            #region Current
            /// <summary>
            /// Get currrent item
            /// </summary>
            public TValue Current
            {
                get
                {
                    return Dictionary[Index];
                }
            }
            object System.Collections.IEnumerator.Current
            {
                get
                {
                    return Dictionary[Index];
                }
            }
            #endregion

            #endregion

            #endregion

            #region IDisposable Members
            /// <summary>
            /// Dipsoe enumerator
            /// </summary>
            public void Dispose()
            {
                Dictionary = null;
            }
            #endregion

            #region IEnumerator Members
            /// <summary>
            /// Move to the next item in dictionary
            /// </summary>
            /// <returns></returns>
            public bool MoveNext()
            {
                return (++Index) < Dictionary.Count;
            }
            /// <summary>
            /// Reset current cursor position to first item in distionary
            /// </summary>
            public void Reset()
            {
                Index = -1;
            }

            #endregion
        } 
        #endregion
    }
}
